package com.agileboot.domain.medicine;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.agileboot.common.constant.Constants;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.medicine.command.ChangeStatusMedicineCommand;
import com.agileboot.domain.medicine.command.MedicineCommand;
import com.agileboot.domain.medicine.db.Medicine;
import com.agileboot.domain.medicine.db.MedicineService;
import com.agileboot.domain.medicine.dto.MedicineDTO;
import com.agileboot.domain.medicine.model.MedicineModel;
import com.agileboot.domain.medicine.model.MedicineModelFactory;
import com.agileboot.domain.medicine.query.MedicineQuery;
import com.agileboot.domain.medicine.vo.MedicineInfoVO;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class MedicineApplicationService {

    private final RabbitTemplate rabbitTemplate;

    private final ElasticsearchOperations elasticsearchOperations;

    private final RestHighLevelClient restHighLevelClient;

    private final MedicineService medicineService;

    private final MedicineModelFactory medicineModelFactory;

    public void addMedicine(MedicineCommand command) {
        //校验数据
        //检查数据库数据是否有重复的数据
        //最好实现批量添加
        MedicineModel medicineModel = medicineModelFactory.create();
        medicineModel.loadAddCommand(command);
        medicineModel.checkMedicineIsUnique();
        medicineModel.checkMedicineIsEmpty(medicineModel);
        medicineModel.insert();
    }

    public PageDTO<MedicineDTO> getMedicineList(MedicineQuery medicineQuery) {
        Page<Medicine> page = medicineService.getMedicineList(medicineQuery.toPage(), medicineQuery.toQueryWrapper());
        List<MedicineDTO> records = page.getRecords().stream().map(MedicineDTO::new).collect(Collectors.toList());
        return new PageDTO<>(records, page.getTotal());
    }

    public MedicineDTO getMedicineDetail(Long id) {
        Assert.isTrue(StringUtils.isNotBlank(String.valueOf(id)), "请求参数不能为空");
        Medicine medicine = medicineModelFactory.loadById(id);
        return BeanUtil.copyProperties(medicine, MedicineDTO.class);
    }

    public void editMedicineDetail(MedicineDTO medicineDTO) throws Exception {
        //参数校验
        if (Objects.isNull(medicineDTO)) {
            throw new ApiException(ErrorCode.Business.REQUEST_FIELD_EMPTY);
        }
        //校验不能为空的参数

        //bean拷贝
        Medicine medicine = BeanUtil.copyProperties(medicineDTO, Medicine.class);
        medicineService.updateById(medicine);
        Medicine medicine1 = medicineService.getById(medicineDTO.getId());
        //校验该草药是否发布
        if (medicine1.getStatus() == 1){
            //如果发布了之后就同步修改es，否则不做操作
            updateDocumentById(Constants.ESIndexName, medicineDTO.getId(),medicine1);
        }
    }


    public void updateDocumentById(String indexName, String id, Medicine medicine) throws IOException {
        UpdateRequest request = new UpdateRequest(indexName, id)
                .doc(JSON.toJSONString(medicine), XContentType.JSON);
        UpdateResponse response = restHighLevelClient.update(request, RequestOptions.DEFAULT);
        // 处理更新响应
        // 可以根据需要在此处进行日志记录或其他操作
        System.out.println("Update Response: " + response);
    }

    public void delMedicineDetail(List<Long> idList) {
        if (CollUtil.isEmpty(idList)) {
            throw new ApiException(ErrorCode.Business.COMMON_BULK_DELETE_IDS_IS_INVALID);
        }
        HashSet<Long> ids = new HashSet<>(idList);
        medicineService.removeBatchByIds(ids);
    }

    public void changeMedicineStatus(ChangeStatusMedicineCommand command) {
        MedicineModel medicineModel = medicineModelFactory.loadById(command.getId());

        medicineModel.setStatus(command.getStatus());
        medicineModel.updateById();

    }

    /**
     * 同步草药数据到ES
     */
    public void syncMedicineToElasticSearch() throws Exception {
        //删除es中对应索引下的所有数据
        if (!deleteESIndex()) {
            throw new ApiException(ErrorCode.Business.ES_ID_NO_EXIST);
        }
        //创建一个新的索引
        addIndex();
        //从数据库中取数据
        //在这里要考虑到数据量过大时，会出下你OOM
        //1、先查询出所有符合要求的数据条数(找出已经发布的数据)
        LambdaQueryWrapper<Medicine> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Medicine::getStatus,1);
        long medicineNum = medicineService.count(queryWrapper);
        //2、多线程（池化技术）做分页--（分批处理）
        int maxSize = 1000;
        int size = (int) Math.ceil((double) medicineNum / maxSize);
        if (size<=0){
            throw new ApiException(ErrorCode.Business.DATA_COUNT_LESS_EQUAL_0);
        }
        for (int i = 0; i < size; i++) {
            Page<Medicine> page = new Page<>(i, maxSize);
            Page<Medicine> medicinePage = medicineService.page(page, queryWrapper);
            //同步数据到es
            syncMedicineDataToES(medicinePage.getRecords());
        }


        //删除之后重新创建一个新的索引，将数据塞进去
//        String orderId = UUID.randomUUID().toString();
//        System.out.println("订单生成功:" + orderId);
//        //交换机名字
//        String exchangeName = "direct_sync_data_es";
//        //路由名称
//        String routingKey = "sync:data";
//        rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId);
    }

    private void addIndex() throws IOException {
        // 获取索引客户端
        IndicesClient indicesClient = restHighLevelClient.indices();
        // 创建索引请求对象: 传入一个索引名称
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(Constants.ESIndexName);
        // 执行创建索引的请求
        // RequestOptions.DEFAULT 指定名称的配置项
        CreateIndexResponse response = indicesClient.create(createIndexRequest, RequestOptions.DEFAULT);
        // 获取响应信息
        String index = response.index();
        System.out.println("索引名称: " + index);
        // 索引是否创建成功
        System.out.println(response.isAcknowledged());
    }

    /**
     * 批量同步中草药数据到es
     * @param ids
     * @throws Exception
     */
    public void syncMedicineToESByIds(List<Long> ids) throws Exception {
        HashSet<Long> list = new HashSet<>(ids);
        if (CollUtil.isEmpty(list)) {
            throw new ApiException(ErrorCode.Business.COMMON_BULK_DELETE_IDS_IS_INVALID);
        }
        List<Medicine> medicines = medicineService.listByIds(list);
        syncMedicineDataToES(medicines);
    }


    //删除elasticsearch的索引
    private boolean deleteESIndex() throws IOException {
        // 获取索引客户端
        IndicesClient indicesClient = restHighLevelClient.indices();
        // 创建删除索引的请求
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(Constants.ESIndexName);
        // 创建执行删除索引的请求
        AcknowledgedResponse response = indicesClient.delete(deleteIndexRequest, RequestOptions.DEFAULT);
        // 索引是否创建成功
        return response.isAcknowledged();
    }


    //新增->同步数据----》ES
    private Integer syncMedicineDataToES(List<Medicine> commands) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");
        // 批量请求处理
        for (Medicine command : commands) {
            bulkRequest.add(
                    // 这里是数据信息
                    new IndexRequest("medicine")
                            .id(command.getId().toString())
                            .source(JSON.toJSONString(command), XContentType.JSON)
            );
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return bulk.status().getStatus();
    }
}
